package ws.very.util.aws.cloudsearch

import java.net.URLEncoder
import ws.very.util.lang.IOs
import java.net.URI

trait AwsCSSearch {

  def searchEndPoint: S
  val urlHead = "http://" + searchEndPoint + """/2011-02-01/search?"""
  case class Search(query: S = null, returnFields: Iterable[S] = Iterable(), rank: S = null, size: I = 0, start: I = 0, descRank: B = false) {
    def queryM = Option(query).map { w => "q" -> URLEncoder.encode(w,"utf-8") }
    def returnFieldsM = if (returnFields)
      Some("return-fields" -> returnFields.mkString(","))
    else None
    def rankM = Option(rank).map { r => "rank" -> (if (descRank) "-" + r else r)
    }
    def sizeM = if (size != 0) Some("size" -> size) else None
    def startS = if (start != 0) Some("start" -> start) else None

    def sum = Array(queryM, returnFieldsM, rankM, sizeM, startS)
    def toTailUrl = sum.filter { _ != None }.map { _.map { case (n, v) => s"$n=$v" }.get }.mkString("&")
  }

  def ?(s: Search) = {
    val u = URI.create(s"""$urlHead${s.toTailUrl}""").toURL()

    //println(u)
    val content = IOs.net.readUrlBytes((u))
    respObj(new String(content, "utf-8"))
  }
  def respObj(s: S) = new {
    //println(s)
    import org.json4s._
    import org.json4s.native.JsonMethods._
    implicit val formats = DefaultFormats
    lazy val jsonVal = parse(s)
    lazy val hits = new {
      lazy val hitsJsonVal = jsonVal \ "hits" //\ classOf[JObject]
      lazy val found = (hitsJsonVal \ "found").extract[I] // \ classOf[JInt]
      lazy val start = (hitsJsonVal \ "start").extract[I] //\\ classOf[JInt]
      lazy val hit = new {
        lazy val hitJsonObj = (hitsJsonVal \ "hit").extract[List[JObject]]
        lazy val hitList = hitJsonObj.map { j =>
          new {
            val json = j
            val id = (j \ "id").extract[S]
            val data = j \ "data"
          }
        }
      }

    }

  }

  /**
   * 新增加索引字段或修改字段索引类型后，并不会马上生效，在新索引字段生效前仍可用原来的索引行进查询操作
   *
   * 调整或优化索引查询 分析查询延迟、减少点击次数、简化布尔查询
   *
   * 查询 可以使用 +(And) | (Or) -(Not)选项操作来做查询
   *
   * 如以建实例中1002跟1003数据为例
   *
   * 1 search?q='scala' search?bq='scala' search?bq(or q)=scala 可以搜索到1003的数据(不需要指定索引字段,不知道是按那个字段搜索到的)
   *
   * 2 search?q='dianying|scala‘ 搜索到两条数据(不需要指定索引字段) 注意:不能是bq查询
   *
   * 3 search?bq=i_name:'scala java  查询i_name中包含scala和java的文档
   *
   * 4 search?bq=i_name:'scala|java' 使用bq查询需要指定索引字段的键，结果跟2相同
   *
   * 5 search?bq=i_name:'"scala,java"|"dianying,dapian"'数据跟文档案不一样 可以搜索但没结果。猜想应该是文本类型的搜索
   *
   * //没测试明白6 search?bq='info_hash:-scala+info_hash|scala' 以样本数据为例的这样的查询有问题
   * //  可以用search?bq='info_hash:-scala+info_hash' 查询结果为1003
   *  "(or i_name:'dianying' (not comment:'language'))" 这样测试结果返回1002说明 -(not) 是comment不包含'language'但可以符合i_name包含'dianying'的意思，(但上面的返回结果有点问题可能跟样本数据有关)
   *
   * 7 search?bq='i_name:-diany*+s*|j*' 返回1002数据
   *
   * 8 search?bq=(and info_hash:'scala' i_name:'scala')可以使用布尔查询操作符拼接两个索引字段的交集 返回1003
   * "(and info_hash:'dianying' i_name:'scala')" 无返回结果
   */
  //
  //search?q=star&return-fields=n,ih
  //search?q=star&return-fields=n,ih&rank=-ut
  //&size=1000&start=50
  //search?bq=ih:'39E22335BD3D2BED5BAEB6F055E28951A30AB699'&return-fields=n,ih&&rank=-ut&size=1000&start=0

  /**
   * {
   * rank: "-text_relevance",//
   * match-expr: "(label 'star')",
   * hits: {
   * found: 7138,//找到共
   * start: 0,
   * hit: [
   * {
   * id: "08d4cd50a8d511a19b3b60d0f538d46615cb8862"
   * }
   * ]
   * },
   * info: {
   * rid: "ee6c7090a20a654dea72cd4a5b93e477ad9a40387058f254454df291a645098a9d0dc6ce065a3719",
   * time-ms: 20,
   * cpu-time-ms: 0
   * }
   * }
   */

}